<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<title>Making a character that can jump</title>
<link href="css/tutorial.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="css/precodeformat.js"></script>
</head>

<body onload="PreCodeFormat()">
<div id="content">
<h1>Making a character that can jump</h1>

<p>
<strong><span class="dropcap">T</span>his tutorial</strong> will show you how you can 
create a character that can jump. We will create a simple background with a level
"ground" and allow the player to move left and right along this ground. When the
up-arrow is pressed, the player will jump up and then be pulled back down to the ground
by our simulated gravity.
</p>

<p class="alert">
This tutorial has been tested with
<a href="http://www.devkitpro.org">devkitARM release 26</a>
and <a href="http://code.google.com/p/spritely">Spritely version 0.19.20 beta</a>
and verified to work for both GBA and NDS projects.
</p>

<h2>Step 1 : Create a new project</h2>

<p>
Create a new project: <tt>c:\gamedev\projects\jump</tt>.
</p>

<h2>Step 2 : Create a sprite</h2>


<p>
Create a sprite for player. Make sure that the image touches the
bottom of the sprite so that we can easily align the player on
the ground that we're going to create.
</p>

<p><img src="jumping/sprite.png" /></p>

<h2>Step 3 : Create a background with a level ground</h2>




<p>
Take the default background sprite and fill it with an appropriate sky color.
</p>

<p>
Add a new 1x1 background tile and fill it with a ground color.
Draw this tile onto the bottom part of the background map to form a 
solid horizontal ground.
</p>

<p><img src="jumping/background_small.png" /></p>

<h2>Step 4 : Save/Export/Build project</h2>


<p>
Save, export, build and run the ROM. The player sprite will appear
floating up in the upper left corner.
We need to change its initial position so that it starts out on the
ground level.
</p>


<p><img src="jumping/ss_01.png" /></p>

<h2>Step 5 : Move the player so that it starts at ground level</h2>

<p>
To do this, we need to figure out:
</p>

<ul>
<li>Where is the "ground level" on the screen</li>
<li>How tall is the player sprite</li>
</ul>

<p>
In <tt>game_state.h</tt>, find the following code:
</p>

<p class="filename"><code><b>game_state.h</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 39 - 41:</p>
<pre class="code">
	// The (x,y) location of the object representing the player.
	int _xPlayer, _yPlayer;
};
</pre>

<p>
This code stores the player's location. Add the following lines to make
space to store the player height and ground level:
</p>

<p class="filename"><code><b>game_state.h</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 39 - 47:</p>
<pre class="code">
	<disabled/>// The (x,y) location of the object representing the player.
	<disabled/>int _xPlayer, _yPlayer;
<mark type="plus"/>
<mark type="plus"/>	// Height of the player sprite.
<mark type="plus"/>	int _yPlayerHeight;
<mark type="plus"/>
<mark type="plus"/>	// The y-coord of ground level.
<mark type="plus"/>	int _yGroundLevel;
<disabled/>};
</pre>

<p>
To initialize these values, go to the initialize code in <tt>game_state.cpp</tt>:
</p>

<p class="filename"><code><b>game_state.cpp</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 48 - 54:</p>
<pre class="code">
	// Initialize the objects for the first level.
	InitObject(kObj_Player, 0);

	// Set the initial location of each object.
	_xPlayer = 0;
	_yPlayer = 0;
	MoveObjectTo(kObj_Player, _xPlayer, _yPlayer);
</pre>

<p>
and make the following changes:
</p>

<p class="filename"><code><b>game_state.cpp</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 48 - 58:</p>
<pre class="code">
	<disabled/>// Initialize the objects for the first level.
	<disabled/>InitObject(kObj_Player, 0);
	<disabled/>
<mark type="plus"/>	// Calculate the ground level.
<mark type="plus"/>	_yPlayerHeight = GetObjectHeight(kObj_Player);
<mark type="plus"/>	_yGroundLevel = 128;
<mark type="plus"/>	
	<disabled/>// Set the initial location of each object.
	<disabled/>_xPlayer = 0;
<mark type="arrow"/>	_yPlayer = _yGroundLevel - _yPlayerHeight;
	<disabled/>MoveObjectTo(kObj_Player, _xPlayer, _yPlayer);
</pre>

<p>
Now you can build and run to see impact of your changes.
</p>

<p>
If you drew your ground at a different level, you may have to change
<tt>128</tt> as appropriate.
</p>


<p><img src="jumping/ss_02.png" /></p>

<h2>Step 6 : Disable the up/down arrow buttons</h2>

<p>
By default, the game the Spritely creates allows you to move the player in all
4 directions: up, down, right and left. Since we want to restrict the player to 
moving left and right only, we need to disable the support for moving up and down.
</p>

<p>
In <tt>game_state.cpp</tt>, find:
</p>

<p class="filename"><code><b>game_state.cpp</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 77 - 89:</p>
<pre class="code">
	<disabled/>// The arrow keys are used to move the current object.
	<disabled/>// We use CheckKeyHeld() because we want the action to repeat as long
	<disabled/>// as the player is holding down the button.
	<disabled/>int dx = 0;
	<disabled/>int dy = 0;
	<disabled/>if (CheckKeyHeld(KEY_LEFT))
		<disabled/>dx = -1;
	<disabled/>if (CheckKeyHeld(KEY_RIGHT))
		<disabled/>dx = 1;
<mark type="cross"/>	if (CheckKeyHeld(KEY_UP))
<mark type="cross"/>		dy = -1;
<mark type="cross"/>	if (CheckKeyHeld(KEY_DOWN))
<mark type="cross"/>		dy = 1;
</pre>

<p>
and remove the 4 lines that handle KEY_UP and KEY_DOWN.
</p>

<p>
If you build and run now, the player will only be able to move left and right.
</p>


<h2>Step 7 : Initial attempt at a jump</h2>

<p>
We want the player to jump when we press the up arrow, so let's add support for that.
</p>

<p>
In <tt>game_state.cpp</tt>, find the code that handles the left and
right arrows and add the following code:
</p>

<p class="filename"><code><b>game_state.cpp</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 87 - 97:</p>
<pre class="code">
	<disabled/>// Handle the player pressing the 'A' button.
	<disabled/>// We use CheckKeyPress() because we *don't* want the action to repeat
	<disabled/>// unless the player presses the 'A' button multiple times.
	<disabled/>if (CheckKeyPress(KEY_A)) {
		<disabled/>// ToDo: Add code to respond to 'A' button press here.
	<disabled/>}
<mark type="plus"/>
<mark type="plus"/>	// Handle the player jump.
<mark type="plus"/>	if (CheckKeyPress(KEY_UP)) {
<mark type="plus"/>		dy = 10;
<mark type="plus"/>	}
</pre>


<p>
If you run your game now, it's doesn't quite work the way we want - the player jumps up
10 pixels and then hovers above the ground. Jumping again has the player going up
even higher. If you keep going, the player can jump
off the top of the screen.
</p>

<p>
The problem is the we're simply adjusting the y-position of the player and 
not keeping track of the player's velocity. We'll fix that in the next step.
</p>

<h2>Step 8 : Keeping track of velocity</h2>

<p>
If we want to keep track of the player's velocity, we'll need a place to store
the current velocity value.
</p>

<p>
We define this storage space in <tt>game_state.h</tt>.
Add 2 new values to the <tt>GameState</tt> class:
</p>

<p class="filename"><code><b>game_state.h</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 39 - 53:</p>
<pre class="code">
	<disabled/>// The (x,y) location of the object representing the player.
	<disabled/>int _xPlayer, _yPlayer;
	<disabled/>
	<disabled/>// Height of the player sprite.
	<disabled/>int _yPlayerHeight;
	<disabled/>
	<disabled/>// The y-coord of ground level.
	<disabled/>int _yGroundLevel;
<mark type="plus"/>	
<mark type="plus"/>	// Is the player currently jumping?
<mark type="plus"/>	bool _isjumping;
<mark type="plus"/>	
<mark type="plus"/>	// Current jump velocity.
<mark type="plus"/>	int _yVelocity;
<disabled/>};
</pre>

<p>
These values keep track of the velocity (only in the y-direction since we're only concerned
about vertical jumps) and also whether or not the player is currently jumping.
</p>

<p>
Whenever we add new variables, we need to initialize them. Go to <tt>game_state.cpp</tt>
and add the following lines.
</p>

<p class="filename"><code><b>game_state.cpp</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 48 - 64:</p>
<pre class="code">
	<disabled/>// Initialize the objects for the first level.
	<disabled/>InitObject(kObj_Player, 0);
	<disabled/>
	<disabled/>// Calculate the ground level.
	<disabled/>_yPlayerHeight = GetObjectHeight(kObj_Player);
	<disabled/>_yGroundLevel = 128;
	<disabled/>
	<disabled/>// Set the initial location of each object.
	<disabled/>_xPlayer = 0;
	<disabled/>_yPlayer = _yGroundLevel - _yPlayerHeight;
	<disabled/>MoveObjectTo(kObj_Player, _xPlayer, _yPlayer);
<mark type="plus"/>	
<mark type="plus"/>	_isjumping = false;
<mark type="plus"/>	_yVelocity = 0;
	<disabled/>
	<disabled/>// TODO: Add more initialization for level 1 here.
<disabled/>}
</pre>

<p>
Now we just need to adjust our jumping code. Find:
</p>

<p class="filename"><code><b>game_state.cpp</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 97 - 100:</p>
<pre class="code">
	// Handle the player jump.
	if (CheckKeyPress(KEY_UP)) {
		dy = 10;
	}
</pre>

<p>
and change it to:
</p>

<p class="filename"><code><b>game_state.cpp</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 97 - 102:</p>
<pre class="code">
	<disabled/>// Handle the player jump.
	<disabled/>if (CheckKeyPress(KEY_UP)) {
<mark type="arrow"/>		_yVelocity = 10;
<mark type="plus"/>		_isjumping = true;
	<disabled/>}
<mark type="plus"/>	dy = -_yVelocity;
</pre>

<p>
This code sets the player's upward velocity to 10 and sets the <tt>_isjumping</tt>
boolean to record that the player is in the middle of a jump.
</p>


<p>
Now if you build and run, the player should jump up.  But it keeps going up and flies off
the top of the screen. The problem is that we've given the player a velocity (up)
but we have nothing that pulls the player back
down to the ground. On Earth, that problem is solved by using gravity, so let's add
some gravity to our game world.
</p>

<h2>Step 9 : Adding gravity</h2>

<p>
Near the end of <tt>game_state.cpp</tt> add the following block of code
after the code to move the player:
</p>

<p class="filename"><code><b>game_state.cpp</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 104 - 127:</p>
<pre class="code">
	<disabled/>// If we need to move the player.
	<disabled/>if (dx != 0 || dy != 0) {
		<disabled/>// Record the player's new location.
		<disabled/>_xPlayer += dx;
		<disabled/>_yPlayer += dy;
	<disabled/>
		<disabled/>// Move the player to the new location.
		<disabled/>MoveObjectTo(kObj_Player, _xPlayer, _yPlayer);
	<disabled/>}
	<disabled/>
<mark type="plus"/>	// If the player is above ground, apply gravity.
<mark type="plus"/>	if (_yPlayer + _yPlayerHeight &lt; _yGroundLevel) {
<mark type="plus"/>		// Apply gravity by reducing upward velocity.
<mark type="plus"/>		_yVelocity--;
<mark type="plus"/>	} else {
<mark type="plus"/>		// Player is on the ground, so stop jumping.
<mark type="plus"/>		_yVelocity = 0;
<mark type="plus"/>		_isjumping = false;
<mark type="plus"/>		// Force player to be exactly at ground level.
<mark type="plus"/>		_yPlayer = _yGroundLevel - _yPlayerHeight;
<mark type="plus"/>		MoveObjectTo(kObj_Player, _xPlayer, _yPlayer);
<mark type="plus"/>	}
<mark type="plus"/>
	<disabled/>// TODO: Add additional game state updates for level 1 here.
<disabled/>}
</pre>

<p>
This code checks to see if the player is above the ground level and, if so, it applies
gravity using <tt>_yVelocity--;</tt>. This code reduces the upward velocity by 1
until the player comes back to the ground.
</p>

<p>
Once the player reaches the ground we set the velocity to 0 and record that the player
is no longer jumping (so that they can jump again). We also force the player
to be at ground level because mid-air jumps can make the player mis-aligned
and have them try to go below ground level.
</p>


<p><img src="jumping/ss_03.png" /></p>

<p>
This "time-lapse" screenshot shows the path that the player takes when moving to 
the right while jumping.
</p>

<h2>Step 10 : Disable mid-air jumps</h2>

<p>
We can disable mid-air jumps by changing:
</p>

<p class="filename"><code><b>game_state.cpp</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 97 - 98:</p>
<pre class="code">
	// Handle the player jump.
	if (CheckKeyPress(KEY_UP)) {
</pre>

<p>
to:
</p>

<p class="filename"><code><b>game_state.cpp</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 97 - 98:</p>
<pre class="code">
	<disabled/>// Handle the player jump.
<mark type="arrow"/>	if (CheckKeyPress(KEY_UP) &amp;&amp; !_isjumping) {
</pre>


<h2>Finished!</h2>

<p>
Now that we've added gravity, we're done. But we can make additional modifications
if we want.
</p>

<p>
We can can adjust the height of the jump by changing the initial velocity.
</p>

<p class="filename"><code><b>game_state.cpp</b></code>&nbsp;&nbsp;&mdash;&nbsp;&nbsp;Lines 97 - 101:</p>
<pre class="code">
	// Handle the player jump.
	if (CheckKeyPress(KEY_UP) &amp;&amp; !_isjumping) {
<mark type="arrow"/>		_yVelocity = 10;
		_isjumping = true;
	}
</pre>

<div id="filelink_bkgd"><div id="filelink">
<h1>Links to completed project</h1>
<p>GBA:</p>
<ul>
<li><a href="jumping/gba/game_state.h">game_state.h</a></li>
<li><a href="jumping/gba/game_state.cpp">game_state.cpp</a></li>
<li><a href="jumping/gba/jump.gba">jump.gba</a></li>
</ul>
<p>NDS:</p>
<ul>
<li><a href="jumping/nds/game_state.h">game_state.h</a></li>
<li><a href="jumping/nds/game_state.cpp">game_state.cpp</a></li>
<li><a href="jumping/nds/jump.nds">jump.nds</a></li>
</ul>
</div></div>

<div id="footer_bkgd"><div id="footer">
<p>Copyright &copy;2008-9 Gary Kacmarcik</p>
</div></div>

</div>

<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-1163903-2");
pageTracker._trackPageview();
} catch(err) {}</script>

</body>
</html>
